﻿using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text.RegularExpressions;
using UtilZ.Dotnet.Ex.Attributes;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Log;
using UtilZ.Dotnet.Ex.Model;
using UtilZ.Dotnet.WindowsDesktopEx.Winform;

namespace WinFormApp
{
    unsafe public partial class UCUdpSendControl : UserControl
    {
        public UCUdpSendControl()
        {
            InitializeComponent();
        }


        private IPEndPoint _remoteIPEndPoint = null;
        private ThreadEx _sendThread = null;
        private System.Windows.Forms.Timer _showSendDataLengthInfoTimer;
        private SendMode _sendMode = SendMode.Sequent;

        private void UCUdpSendControl_Load(object sender, EventArgs e)
        {
            try
            {
                numSendDatapackgeSize.Maximum = NetHelper.UDP_PACKAGE_MAX;
                //numSendDatapackgeSize.Minimum = 1500;
                numSendDatapackgeSize.Minimum = 1;
                numSendDatapackgeSize.Value = 8192;

                List<NameValue<bool>> list = new List<NameValue<bool>>()
                {
                    new NameValue<bool>("内存数据",true),
                    new NameValue<bool>("文件数据",false),
                };
                DropdownBoxHelper.BindingIEnumerableGenericToComboBox(comboBoxSendDataType, list);

                IPAddress[] ipaddrArr = NetHelper.GetAllIPV4();
                foreach (var ipaddr in ipaddrArr)
                {
                    comboBoxSendDataIP.Items.Add(ipaddr);
                }
                if (comboBoxSendDataIP.Items.Count > 0)
                {
                    comboBoxSendDataIP.SelectedIndex = 0;
                }

                DropdownBoxHelper.BindingEnumToComboBox<SendMode>(comboBoxSendMode, SendMode.Sequent);

                this._showSendDataLengthInfoTimer = new System.Windows.Forms.Timer();
                this._showSendDataLengthInfoTimer.Interval = 1000;
                this._showSendDataLengthInfoTimer.Tick += _showSendDataLengthInfoTimer_Tick;

                this._sendThread = new ThreadEx(this.SendThreadMethod);
                this.UpdateReceiveDataEndPoint();
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }

        private void comboBoxSendMode_SelectedIndexChanged(object sender, EventArgs e)
        {
            this._sendMode = DropdownBoxHelper.GetEnumFromComboBox<SendMode>(comboBoxSendMode);
            if (this._sendMode == SendMode.Sequent)
            {
                btnStepSend.Enabled = false;
                btnSend.Enabled = true;
                numSendThreadCount.Enabled = true;
                bntRestStepSend.Enabled = false;
            }
            else
            {
                btnStepSend.Enabled = true;
                btnSend.Enabled = false;
                numSendThreadCount.Enabled = false;
                bntRestStepSend.Enabled = true;
                this.UpdateStepUdpClient();
            }
        }

        private void numSendDataPort_ValueChanged(object sender, EventArgs e)
        {
            if (this._sendMode == SendMode.Step)
            {
                this.UpdateStepUdpClient();
            }
        }

        private void comboBoxSendDataIP_TextChanged(object sender, EventArgs e)
        {
            if (this._sendMode == SendMode.Step)
            {
                this.UpdateStepUdpClient();
            }
        }

        private void txtRcvDataIP_TextChanged(object sender, EventArgs e)
        {
            this.UpdateReceiveDataEndPoint();
        }

        private void numRcvDataPort_ValueChanged(object sender, EventArgs e)
        {
            this.UpdateReceiveDataEndPoint();
        }

        private void UpdateReceiveDataEndPoint()
        {
            if (string.IsNullOrWhiteSpace(txtRcvDataIP.Text))
            {
                return;
            }

            try
            {
                string ip = txtRcvDataIP.Text.Trim();
                if (!Regex.IsMatch(ip, RegexConstant.IPV4Reg))
                {
                    return;
                }

                this._remoteIPEndPoint = new IPEndPoint(IPAddress.Parse(ip), (int)numRcvDataPort.Value);
                Loger.Info($"接收数据IPEndPoint更新为:{NetHelper.GetIPPortString(this._remoteIPEndPoint)}");
            }
            catch (SocketException)
            {

            }
            catch (Exception ex)
            {
                Loger.Warn(ex);
            }
        }



        private void btnChioceFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Multiselect = false;
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                txtFilePath.Text = ofd.FileName;
            }
        }

        private void comboBoxSendDataType_SelectedIndexChanged(object sender, EventArgs e)
        {
            var selestedItem = DropdownBoxHelper.GetGenericFromComboBox<NameValue<bool>>(comboBoxSendDataType);
            if (selestedItem.Value)
            {
                //内存数据
                txtFilePath.Enabled = false;
                btnChioceFile.Enabled = false;
                numSendDataSize.Enabled = true;
                numSendPostion.Enabled = false;
            }
            else
            {
                //文件数据
                txtFilePath.Enabled = true;
                btnChioceFile.Enabled = true;
                numSendDataSize.Enabled = false;
                numSendPostion.Enabled = true;
            }
        }



        private void btnSend_Click(object sender, EventArgs e)
        {
            try
            {
                if (this._reader != null)
                {
                    this._reader.Dispose();
                    this._reader = null;
                }

                if (this._sendThread.IsRuning)
                {
                    this._sendThread.Stop();
                    Loger.Info("已停止发送");
                    btnSend.Text = "发送";
                    return;
                }

                SendDataPara sendData;
                var selestedItem = DropdownBoxHelper.GetGenericFromComboBox<NameValue<bool>>(comboBoxSendDataType);
                if (selestedItem.Value)
                {
                    //内存数据
                    sendData = new SendDataPara((int)numSendDataSize.Value);
                }
                else
                {
                    //文件数据
                    string filePath = txtFilePath.Text.Trim();
                    if (string.IsNullOrWhiteSpace(filePath))
                    {
                        Loger.Warn("发送文件路径不能为空");
                        return;
                    }

                    if (!File.Exists(filePath))
                    {
                        Loger.Warn("发送文件不存在");
                        return;
                    }

                    sendData = new SendDataPara(filePath, (long)numSendPostion.Value);
                }

                sendData.SendIPAddress = IPAddress.Parse(comboBoxSendDataIP.Text);
                sendData.SendDataPort = (int)numSendDataPort.Value;
                sendData.RcvDataEndPoint = this._remoteIPEndPoint;
                sendData.SendDatapackgeSize = (int)numSendDatapackgeSize.Value;
                sendData.SendIntervaMS = (int)numSendIntervalMS.Value;
                sendData.SendThreadCount = (int)numSendThreadCount.Value;
                sendData.DataFramMode = checkBoxDataFramMode.Checked;
                this._sendThread.Start(sendData);
                this.SetControlEnable(false);
                Loger.Info("已开始发送");
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }

        private void SetControlEnable(bool enable)
        {
            if (this._sendMode == SendMode.Sequent)
            {
                numSendDataPort.Enabled = false;
                numSendThreadCount.Enabled = enable;
                bntRestStepSend.Enabled = enable;
            }
            else
            {
                numSendDataPort.Enabled = enable;
                numSendThreadCount.Enabled = false;
                bntRestStepSend.Enabled = true;
            }


            comboBoxSendDataIP.Enabled = enable;
            //numSendDataPort.Enabled = enable;
            numSendIntervalMS.Enabled = enable;

            txtRcvDataIP.Enabled = enable;
            numRcvDataPort.Enabled = enable;
            comboBoxSendDataType.Enabled = enable;
            txtFilePath.Enabled = enable;
            numSendDataSize.Enabled = enable;
            numSendDatapackgeSize.Enabled = enable;

            if (enable)
            {
                btnSend.Text = "发送";
                this._showSendDataLengthInfoTimer.Stop();
                this._showSendDataLengthInfoTimer_Tick(this, null);
            }
            else
            {
                btnSend.Text = "停止";

                this._lastTimestamp = TimeEx.GetTimestamp(DateTime.Now);
                this._showSendDataLengthInfoTimer.Start();
            }
        }


        private int _sendPort = 21000;
        private void SendThreadMethod(ThreadPara threadPara)
        {
            try
            {
                SendDataPara sendData = (SendDataPara)threadPara.Para;
                this._sendedDataLength = 0L;
                if (sendData.Sendtype)
                {
                    Loger.Info("开始发送内存数据..");
                }
                else
                {
                    Loger.Info("开始发送文件数据..");
                }



                if (sendData.SendThreadCount > 1)
                {
                    this._sendPort = 21000;
                    Task[] sendTaskArr = new Task[sendData.SendThreadCount];
                    for (int i = 0; i < sendTaskArr.Length; i++)
                    {
                        sendTaskArr[i] = new Task(() =>
                        {
                            UdpClient udpClient = null;
                            try
                            {
                                while (true)
                                {
                                    try
                                    {
                                        udpClient = new UdpClient(new IPEndPoint(sendData.SendIPAddress, Interlocked.Increment(ref this._sendPort)));
                                        udpClient.Connect(sendData.RcvDataEndPoint);
                                        break;
                                    }
                                    catch (SocketException)
                                    {
                                        continue;
                                    }
                                }

                                if (sendData.Sendtype)
                                {
                                    this.SendData(threadPara, sendData, udpClient);
                                }
                                else
                                {
                                    this.SendFile(threadPara, sendData, udpClient);
                                }
                            }
                            catch (Exception ex)
                            {
                                Loger.Error(ex);
                            }
                            finally
                            {
                                if (udpClient != null)
                                {
                                    udpClient.Dispose();
                                }
                            }
                        }, threadPara.Token, TaskCreationOptions.LongRunning);

                        sendTaskArr[i].Start();
                    }
                    Task.WaitAll(sendTaskArr);
                }
                else
                {
                    using var udpClient = new UdpClient(new IPEndPoint(sendData.SendIPAddress, sendData.SendDataPort));
                    udpClient.Connect(sendData.RcvDataEndPoint);
                    if (sendData.Sendtype)
                    {
                        this.SendData(threadPara, sendData, udpClient);
                    }
                    else
                    {
                        this.SendFile(threadPara, sendData, udpClient);
                    }
                }
                Loger.Info("已发送结束.");
            }
            catch (Exception ex)
            {
                Loger.Error(ex, "发送数据异常");
            }

            this.Invoke(new Action(() =>
            {
                this.SetControlEnable(true);
                this.comboBoxSendDataType_SelectedIndexChanged(this, null);
            }));
        }





        private const string _RCV_TIME_FORMAT = "HH:mm:ss.fff";
        private long _sendedDataLength = 0L;

        private long _lastSendedDataLength = 0L;
        private long _lastTimestamp = 0L;

        private void _showSendDataLengthInfoTimer_Tick(object sender, EventArgs e)
        {
            const int K = 1000;
            var sendDataLength = this._sendedDataLength;

            var currenttimestamp = TimeEx.GetTimestamp();
            var seonds = (double)(currenttimestamp - this._lastTimestamp) / 1000;
            if (seonds < 0.001)
            {
                labelSendStaic.Text = "平均每秒发送数据大小：0MB，0BYTE";
            }
            else
            {
                var newLength = sendDataLength - this._lastSendedDataLength;
                labelSendStaic.Text = $"平均每秒发送数据大小：{Math.Round(newLength / K / K / seonds, 3)}MB，{Math.Ceiling(newLength / seonds)}BYTE";
            }

            this._lastSendedDataLength = sendDataLength;
            this._lastTimestamp = currenttimestamp;
        }

        private void SendFile(ThreadPara threadPara, SendDataPara sendData, UdpClient udpClient)
        {
            byte[] buffer = new byte[sendData.SendDatapackgeSize];
            int sendLength = buffer.Length;
            int sendIntervalMs = sendData.SendIntervaMS;

            using var fileStream = new FileStream(sendData.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
            fileStream.Position = sendData.Postion;
            long modLength;
            int readedLength;
            long beginPos;

            while (!threadPara.Token.IsCancellationRequested)
            {
                try
                {
                    modLength = fileStream.Length - fileStream.Position;
                    if (modLength <= 0)
                    {
                        break;
                    }

                    if (modLength < sendLength)
                    {
                        sendLength = (int)modLength;
                    }

                    beginPos = fileStream.Position;
                    readedLength = fileStream.Read(buffer, 0, sendLength);
                    udpClient.Send(buffer);
                    Loger.Info($"udp发送文件数据{beginPos}-{fileStream.Position}共{buffer.Length}byte到\"{NetHelper.GetIPPortString(udpClient.Client.LocalEndPoint)}\"成功.");
                    Interlocked.Add(ref this._sendedDataLength, buffer.Length);

                    if (sendIntervalMs > 0)
                    {
                        threadPara.WaitOne(sendIntervalMs);
                    }
                }
                catch (Exception exi)
                {
                    Loger.Error(exi);
                }
            }
        }

        private ulong _sendNum = 0L;
        private Stopwatch _stopwatch = new Stopwatch();
        private double _waitStartTime;
        private double _sendIntervalNS = 0d;
        private void SendData(ThreadPara threadPara, SendDataPara sendData, UdpClient udpClient)
        {
            using UnmanagedMemoryPointer<byte> pointer = new UnmanagedMemoryPointer<byte>(sendData.SendDatapackgeSize);
            ReadOnlySpan<byte> buffer = pointer.GetSpan();
            byte value = 0;
            for (int i = 0; i < buffer.Length; i++)
            {
                pointer[i] = value++;
            }

            this._sendIntervalNS = (double)sendData.SendIntervaMS * 10000;
            long totalByteLength = UnitConverter.MByteToByte(sendData.DataSizeMB);
            var mod = totalByteLength % pointer.Capacity;
            if (mod != 0)
            {
                totalByteLength += (pointer.Capacity - mod);
            }
            int sendLength = buffer.Length;

            ulong* sendNumPtr = (ulong*)pointer.DataPtr;

            this._stopwatch.Start();

            while (!threadPara.Token.IsCancellationRequested)
            {
                try
                {
                    *sendNumPtr = Interlocked.Increment(ref this._sendNum);

                    //if (this._sendedDataLength + sendLength >= totalByteLength)
                    //{
                    //    udpClient.Send(buffer.Slice(sendLength));
                    //    Interlocked.Add(ref this._sendedDataLength, buffer.Length);

                    //    if (this._sendedDataLength >= totalByteLength)
                    //    {
                    //        break;
                    //    }
                    //}
                    //else
                    //{
                    udpClient.Send(buffer);
                    Interlocked.Add(ref this._sendedDataLength, buffer.Length);
                    if (this._sendedDataLength >= totalByteLength)
                    {
                        break;
                    }
                    //}

                    if (this._sendIntervalNS > 0)
                    {
                        this._waitStartTime = _stopwatch.Elapsed.TotalNanoseconds;
                        SpinWait.SpinUntil(() =>
                        {
                            return this._stopwatch.Elapsed.TotalNanoseconds - this._waitStartTime > this._sendIntervalNS;
                        });
                        //threadPara.WaitOne(sendIntervalMs);
                    }
                }
                catch (Exception exi)
                {
                    Loger.Error(exi);
                }
            }
            this._stopwatch.Stop();
        }







        private UdpClient _stepUdpClient = null;
        private BinaryReader _reader = null;
        private bool _stepDataFramMode = false;
        private void UpdateStepUdpClient()
        {
            try
            {

            }
            catch (SocketException sex)
            {
                Loger.Warn(sex);
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }


        private void btnStepSend_Click(object sender, EventArgs e)
        {
            try
            {
                if (this._stepUdpClient == null)
                {
                    var sendEndPoint = new IPEndPoint(IPAddress.Parse(comboBoxSendDataIP.Text), (int)numSendDataPort.Value);
                    this._stepUdpClient = new UdpClient(sendEndPoint);
                    Loger.Info($"单步发送数据IPEndPoint更新为:{NetHelper.GetIPPortString(sendEndPoint)}");
                }

                int datapackgeSize = (int)numSendDatapackgeSize.Value;
                byte[] buffer;
                var selestedItem = DropdownBoxHelper.GetGenericFromComboBox<NameValue<bool>>(comboBoxSendDataType);
                if (selestedItem.Value)
                {
                    //内存数据
                    buffer = new byte[datapackgeSize];
                    byte value = 0;
                    for (int i = 0; i < buffer.Length; i++)
                    {
                        buffer[i] = value++;
                    }
                    this._stepUdpClient.Send(buffer, this._remoteIPEndPoint);
                    Loger.Info($"udp发送内存数据到\"{NetHelper.GetIPPortString(this._remoteIPEndPoint)}\"成功.");
                }
                else
                {
                    //文件数据
                    if (this._reader == null)
                    {
                        string filePath = txtFilePath.Text.Trim();
                        if (string.IsNullOrWhiteSpace(filePath))
                        {
                            Loger.Warn("发送文件路径不能为空");
                            return;
                        }

                        if (!File.Exists(filePath))
                        {
                            Loger.Warn("发送文件不存在");
                            return;
                        }

                        var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                        fileStream.Position = (long)numSendPostion.Value;
                        this._reader = new BinaryReader(fileStream);
                        this._stepDataFramMode = checkBoxDataFramMode.Checked;
                    }

                    var beginPos = this._reader.BaseStream.Position;
                    if (this._stepDataFramMode)
                    {
                        int pkLen = this._reader.ReadInt32();
                        if (pkLen <= 0)
                        {
                            Loger.Warn($"从文件中读取的UDP数据帧包长\"{pkLen}\"小于等于0,该值无效,发送失败.");
                            return;
                        }
                        buffer = new byte[pkLen];
                    }
                    else
                    {
                        var modLen = this._reader.BaseStream.Length - this._reader.BaseStream.Position;
                        if (modLen < datapackgeSize)
                        {
                            buffer = new byte[modLen];
                        }
                        else
                        {
                            buffer = new byte[datapackgeSize];
                        }
                    }

                    this._reader.Read(buffer, 0, buffer.Length);
                    this._stepUdpClient.Send(buffer, this._remoteIPEndPoint);

                    if (this._reader.BaseStream.Length - this._reader.BaseStream.Position <= 0)
                    {
                        Loger.Info($"udp发送文件数据{beginPos}-{this._reader.BaseStream.Position}共{buffer.Length}byte到\"{NetHelper.GetIPPortString(this._remoteIPEndPoint)}\"成功,文件数据已发送结束.");
                    }
                    else
                    {
                        Loger.Info($"udp发送文件数据{beginPos}-{this._reader.BaseStream.Position}共{buffer.Length}byte到\"{NetHelper.GetIPPortString(this._remoteIPEndPoint)}\"成功.");
                    }
                }
            }
            catch (Exception ex)
            {
                Loger.Error(ex, "发送udp数据失败");
            }
        }

        private void bntRestStepSend_Click(object sender, EventArgs e)
        {
            if (this._reader != null)
            {
                this._reader.Close();
                this._reader = null;
            }

            if (this._stepUdpClient != null)
            {
                this._stepUdpClient.Dispose();
                this._stepUdpClient = null;
            }

            Loger.Info("单步发送数据重置完成...");
        }







        internal class SendDataPara
        {
            public string FilePath { get; private set; }

            public long Postion { get; private set; }

            public int DataSizeMB { get; private set; }

            public bool Sendtype { get; private set; }
            public IPAddress SendIPAddress { get; internal set; }
            public int SendDataPort { get; internal set; }
            public IPEndPoint RcvDataEndPoint { get; internal set; }
            public int SendDatapackgeSize { get; internal set; }

            public int SendIntervaMS { get; internal set; }
            public int SendThreadCount { get; internal set; }

            public bool DataFramMode { get; internal set; }

            public SendDataPara(string filePath, long postion)
            {
                this.FilePath = filePath;
                this.Postion = postion;
                this.Sendtype = false;
            }

            public SendDataPara(int dataSizeMB)
            {
                this.DataSizeMB = dataSizeMB;
                this.Sendtype = true;
            }
        }

        private enum SendMode
        {
            [DisplayNameEx("连续发送")]
            Sequent,

            [DisplayNameEx("单步发送")]
            Step
        }


    }
}
